#include <signal.h>
#include <sys/types.h>
#include "wrk_process.h"
#include <string.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include "wrk_logger.h"
#include "server.h"
#include <sys/file.h>

typedef struct {
    int     signo;
    char   *signame;
    char   *name;
    void  (*handler)(int signo, siginfo_t *siginfo, void *ucontext);
} wrk_signal_t;

// static void wrk_signal_handler(int signo, siginfo_t *siginfo, void *ucontext);

static void signal_release_handler(int signo, siginfo_t *siginfo, void *ucontext);

static void signal_reload_handler(int signo, siginfo_t *siginfo, void *ucontext);

static void signal_stop_handler(int signo, siginfo_t *siginfo, void *ucontext);

static void signal_exitself_handler(int signo, siginfo_t *siginfo, void *ucontext);

static void signal_spawn_process_handler(int signo, siginfo_t *siginfo, void *ucontext);

static void signal_backtrace_handler(int signo, siginfo_t *siginfo, void *ucontext);

wrk_worker_process_t *wrk_worker_process;

extern char *conf;

extern char *arg0;

wrk_signal_t  signals[] = {
    { wrk_signal_value(WRK_RELEASE_SIGNAL),
      "SIG" wrk_value(WRK_RELEASE_SIGNAL),
      "release",
      signal_release_handler },
    
    { wrk_signal_value(WRK_RELOAD_SIGNAL),
      "SIG" wrk_value(WRK_RELOAD_SIGNAL),
      "reload",
      signal_reload_handler },

    { wrk_signal_value(WRK_TERMINATE_SIGNAL),
      "SIG" wrk_value(WRK_TERMINATE_SIGNAL),
      "stop",
      signal_stop_handler },

    { SIGALRM, "SIGALRM", "", NULL },

    { SIGINT, "SIGINT", "", signal_exitself_handler },

    { SIGIO, "SIGIO", "", NULL },

    { SIGCHLD, "SIGCHLD", "", signal_spawn_process_handler },

    { SIGSYS, "SIGSYS, SIG_IGN", "", NULL },

    { SIGPIPE, "SIGPIPE, SIG_IGN", "", NULL },

    { SIGILL, "SIGILL", "", signal_backtrace_handler },
    { SIGBUS, "SIGBUS", "", signal_backtrace_handler },
    { SIGSEGV, "SIGSEGV", "", signal_backtrace_handler },
    { SIGABRT, "SIGABRT", "", signal_backtrace_handler },
    { SIGFPE, "SIGABRT", "", signal_backtrace_handler },
    { SIGABRT, "SIGSYS", "", signal_backtrace_handler },
    { SIGTTIN, "SIGTTIN", "", signal_backtrace_handler },

    { 0, NULL, "", NULL }
};

void
wrk_init_signals()
{
    wrk_signal_t      *sig;
    struct sigaction   sa;

    for (sig = signals; sig->signo != 0; sig++) {
        wrk_memzero(&sa, sizeof(struct sigaction));
        if (sig->handler) {
            sa.sa_sigaction = sig->handler;
            sa.sa_flags = SA_SIGINFO;

        } else {
            sa.sa_handler = SIG_IGN;
        }

        sigemptyset(&sa.sa_mask);
        if (sigaction(sig->signo, &sa, NULL) == -1) {
            throw std::string("sigaction erro: " + std::string(strerror(errno)));
        }
    }

    LOG_INFO("init signals success");
}

static  void signal_exitself_handler(int signo, siginfo_t *siginfo, void *ucontext)
{
    
    if(server::master_pid == getpid()) {
        kill(server::master_pid, SIGTERM);
    }else
    {
        LOG_ERROR("exception signal captrue, signo=%d", signo);
    }
    
}


static  void signal_backtrace_handler(int signo, siginfo_t *siginfo, void *ucontext)
{
    wrk_signal_t      *sig;
    for (sig = signals; sig->signo != 0; sig++) {
        if(sig->signo==signo) {
            break;
        }
    }

    LOG_ERROR("exception signal captrue, signo=%d, signame=%s", signo, sig->signame);

    //到这个函数是发生了严重错误，直接退出由master重新拉起来, master忽略
    if(server::master_pid != getpid()) exit(-1);
}

static void signal_reload_handler(int signo, siginfo_t *siginfo, void *ucontext)
{
    printf("signal_reload_handler\n");
    int lock_file_fd = open(LOCKFILE, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    if(lock_file_fd<0) {
        printf("can't open %s, error:%s\n", LOCKFILE, strerror(errno));
        exit(-1);
    }

    if(flock(lock_file_fd, LOCK_UN)) {
        printf("unlock %s error, %s\n", LOCKFILE, strerror(errno));
        exit(-1);
    }

    // ftruncate(lock_file_fd, 0);
    printf("arg0:%s, conf:%s\n", arg0, conf);
    pid_t master_pid = getpid();
    pid_t pid;
    if ((pid = fork())<0) {
        printf("fork err, %s\n", strerror(errno));
    }else if(pid==0) {
        int ret = execlp("./dist-wrk", "-c", "src/test.conf", (char *)0);
        if(ret) {
            printf("pid:%d, execlp error: %s\n", getpid(), strerror(errno));
            exit(-1);
        }
        if(kill(master_pid, SIGTERM)) {
            printf("send SIGTERM to pid:%d error, %s\n", master_pid, strerror(errno));
            exit(-1);
        }

        printf("pid:%d, send SIGTERM to pid:%d success\n", getpid(), master_pid);
    }
}

static void signal_release_handler(int signo, siginfo_t *siginfo, void *ucontext) 
{
    for(int i=0; i<MAX_WORKER_PROCESS_NUM; i++) {
        if(wrk_worker_process[i].pid == siginfo->si_pid) {
            LOG_DEBUG("worker[%d] turn to IDEL", siginfo->si_pid);
            wrk_worker_process[i].status = wrk_worker_process_t::IDEL;
            break;
        }
    }
}

// start process that exit one
static void signal_spawn_process_handler(int signo, siginfo_t *siginfo, void *ucontext) 
{
    // todo waitpid
    int status;
    for(int i=0; i<MAX_WORKER_PROCESS_NUM; i++) {
        waitpid(-1, &status, WNOHANG);
        if(wrk_worker_process[i].pid == siginfo->si_pid) {
            // printf("close wrk_worker_process[%d].pid = %d, fd = %d\n", i, wrk_worker_process[i].pid , wrk_worker_process[i].channel[0]);
            close(wrk_worker_process[i].channel[0]);

            server::wrk_spawn_process((void *) (intptr_t) i, "worker process");
        }
    }
}

static void signal_stop_handler(int signo, siginfo_t *siginfo, void *ucontext) 
{
    // 要先屏蔽掉 signal_child_handler 
    signal(SIGCHLD, SIG_IGN);
    for(int i=0; i<MAX_WORKER_PROCESS_NUM; i++) {
        pid_t pid = wrk_worker_process[i].pid;
        kill(pid, SIGKILL);
    }

    unlink(LOCKFILE);
    exit(0);
}

wrk_worker_process_t *get_process_by_taskid(unsigned int taskid) 
{
    wrk_worker_process_t *p = NULL;
    for(int i=0; i<MAX_WORKER_PROCESS_NUM; i++) {
        if(wrk_worker_process[i].taskid == taskid) {
            p = &(wrk_worker_process[i]);
            break;
        }
    }

    return p;
}

wrk_worker_process_t * get_one_idle_worker()
{
    for(int i=0; i<MAX_WORKER_PROCESS_NUM; i++) {
       if (wrk_worker_process[i].status == wrk_worker_process_t::IDEL) {
           return & (wrk_worker_process[i]);
       }
    }

    return NULL;
}


void pr_exit(int status)                                                                                                                                                     
{                                                                                                                                                                             
    if(WIFEXITED(status)) {
        printf("normal termination,exitstatus=%d\n",WEXITSTATUS(status)); 
    } else if(WIFSIGNALED(status)) {
        printf("abnormal termination,signalstatus=%d\n",WTERMSIG(status));
    } else if(WIFSTOPPED(status))  {
        printf("child stopped ,signal number=%d\n", WSTOPSIG(status)); 
    }                                                                                                                                        
}  
